import json
import os
from typing import List, Dict

from pex import toml

from logger import logger

from constants import consts
from tools import webhook
from tools.git_client import GitClient
from sub_events import ALL_REGISTERED_EVENTS

CFG = toml.load('config.toml')
LOGGER = logger.CustomLogger()


def generate_html_table(data: List[Dict],
                        headers: List[str] = None,
                        caption: str = None,
                        border: bool = True,
                        zebra: bool = True
                        ) -> str:
    """
    生成带样式的HTML表格
    :param data: 数据列表(每个字典代表一行)
    :param headers: 自定义表头(默认使用字典键)
    :param caption: 表格标题
    :param border: 是否显示边框
    :param zebra: 是否启用斑马条纹样式
    :return: 完整HTML字符串
    """
    if not data:
        return ""

    headers = headers or list(data[0].keys())
    style = "border-collapse: collapse; width: 100%;"
    if border:
        style += " border: 1px solid #ddd;"

    html = [f'<table style="{style}">']

    if caption:
        html.append(f'<caption style="padding:10px;font-weight:bold">{caption}</caption>')

    # 表头
    html.append('<tr style="background-color:#f2f2f2">')
    for h in headers:
        html.append(f'<th style="padding:12px;text-align:left">{h}</th>')
    html.append('</tr>')

    # 数据行
    for i, row in enumerate(data):
        row_style = "background-color: #f9f9f9" if zebra and i % 2 else ""
        html.append(f'<tr style="{row_style}">')
        for h in headers:
            val = str(row.get(h, ''))
            if h == "details":
                if val == "":
                    val = "-"
                else:
                    val = f"<details><summary><b>🔍 点击查看详情/click to show details</b></summary>{val}</details>"
            html.append(f'<td style="padding:8px;border-bottom:1px solid #ddd">{val}</td>')
        html.append('</tr>')

    html.append('</table>')
    return '\n'.join(html)


class WebHookProcessor:
    def __init__(self, debug_mode):
        self.debug_mode = debug_mode

    def execute_event(self, hook: webhook.WebHook, git_client: GitClient, event_json):
        if not event_json.get("enable"):
            return None
        event_model = ALL_REGISTERED_EVENTS.get(event_json.get("event_class"))
        if not event_model:
            LOGGER.warning(f"[{hook.pr_number}] wrong event {event_model.get('event_class')}, skipping.")
            return None
        branches = event_json.get("branches", [])
        if branches and hook.branch not in branches:
            return None
        event = event_model(debug_mode=self.debug_mode, git_client=git_client)
        event.set_hook(hook)
        if not event.pre_execute():
            return event
        event.execute()
        return event

    def generate_statement(self, event_name_zh, event_name_en, failed=False):
        statement = ""
        if event_name_zh:
            result = "失败" if failed else "成功"
            statement += "%s%s。" % (event_name_zh, result)
        if event_name_en:
            if statement:
                statement += "\n"
            result = "failed" if failed else "pass"
            statement += "%s %s." % (event_name_en, result)
        return statement

    def process_event(self, hook: webhook.WebHook, event):
        git_client = GitClient(self.debug_mode, hook.repo_owner, hook.repo_name)
        check_events = event.get("check_events", [])
        action_events = event.get("action_events", [])
        send_comment = event.get("send_comment", False)
        check_succeed = True
        check_result = []
        # 1. 处理check项
        for check_event_json in check_events:
            check_event = self.execute_event(hook, git_client, check_event_json)
            if not check_event:
                continue
            check_succeed = check_succeed and not check_event.failed
            check_result.append({
                "check type": "%s/%s" % (check_event.event_name_zh, check_event.event_name_en),
                "result": "fail" if check_event.failed else "pass",
                "details": check_event.get_comment()
            })
        # 2. 若check项失败，判断是否需要发送评论，且不执行action项
        event_name_zh = event.get("event_name_zh", "")
        event_name_en = event.get("event_name_en", "")
        check_result_table = generate_html_table(check_result)

        def comment(comments):
            if not comments:
                return
            token = list(CFG.get("accounts", {}).get("notifier").values())
            if not token:
                LOGGER.error(f"[{hook.pr_number}] no token found for notifier")
                return
            if consts.COMMAND_LIST_COMMANDS not in comments:
                comments += (f"\n\n您可通过评论\"self check\"来重新触发加分前检查。\n"
                             f"You can leave a \"self check\" comment to re-trigger check before review.\n"
                             f"您可通过评论\"{consts.COMMAND_LIST_COMMANDS}\"来查询命令列表。\n"
                             f"You can leave a \"{consts.COMMAND_LIST_COMMANDS}\" comment to get commands list.")
            headers = {'Authorization': f'token {token[0]}'}
            comment_data = {'body': comments}
            response = git_client.comment(hook.pr_number, headers=headers, json=comment_data)
            LOGGER.info(f"[{hook.pr_number}] {event_name_en} comment response code {response.status_code}")
            comments_for_log = comments.replace('\U0001f50d', '')
            LOGGER.info(f"[{hook.pr_number}] comment body\n{comments_for_log}")

        if not check_succeed:
            if not send_comment:
                return
            statement = self.generate_statement(event_name_zh, event_name_en, failed=True)
            comments = check_result_table + "\n\n" + statement
            comment(comments)
            return

        # 3. 执行action项
        comments = ""
        for action_event_json in action_events:
            action_event = self.execute_event(hook, git_client, action_event_json)
            if not action_event:
                continue
            if comments:
                comments += "\n\n"
            comments += action_event.get_comment()
        if check_result_table:
            comments = check_result_table + "\n\n" + comments
        comment(comments)

    def process(self, hook: webhook.WebHook):
        current_file = os.path.abspath(__file__)
        dir_path = os.path.dirname(current_file)
        json_file = os.path.join(dir_path, "manifest.json")
        with open(json_file, 'r', encoding='utf-8') as f:
            hook_cfg = json.load(f)
            if hook.hook_name not in hook_cfg:
                return
            if hook.action not in hook_cfg[hook.hook_name]:
                return
            event = None
            if hook.hook_name == consts.HOOK_NAME_NOTE:
                if hook.comment_body not in hook_cfg[hook.hook_name][hook.action]:
                    return
                event = hook_cfg[hook.hook_name][hook.action].get(hook.comment_body)
            if hook.hook_name == consts.HOOK_NAME_MERGE:
                event = hook_cfg[hook.hook_name].get(hook.action)
                if event.get("action_desc") and event.get("action_desc") != hook.get("action_desc"):
                    return
            if not event:
                LOGGER.warning(
                    f"[{hook.pr_number}] no event found, hook_name: {hook.hook_name}, action: {hook.action}.")
                return
            self.process_event(hook, event)
